package com.websocket.exception.service;

import com.websocket.annotation.WsControllerAdvice;
import com.websocket.annotation.WsExceptionHandler;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

public class ExceptionMapping {

    private final List<ExceptionExecution> exHandler = new ArrayList<>();

    public ExceptionMapping(ApplicationContext applicationContext) {
        Map<String, Object> beans = applicationContext.getBeansWithAnnotation(WsControllerAdvice.class);
        registryExHandler(beans);
    }

    private void registryExHandler(Map<String, Object> beans) {
        beans.forEach((name, bean) -> {
            Class<?> cls = bean.getClass();
            Method[] methods = cls.getDeclaredMethods();
            for (Method method : methods) {
                // 跳过静态方法
                if (Modifier.isStatic(method.getModifiers())) continue;

                // 跳过没有 WsMapping 注解的方法
                WsExceptionHandler anno = method.getAnnotation(WsExceptionHandler.class);
                if (anno == null) continue;

                // 跳过没有 Exception 参数的方法
                Parameter[] parameters = method.getParameters();
                Parameter exParam = null;
                for (Parameter parameter : parameters) {
                    if(Exception.class.isAssignableFrom(parameter.getType())) {
                        exParam = parameter;
                        break;
                    }
                }
                if (exParam == null) continue;

                ExceptionExecution exceptionExecution = new ExceptionExecution(exParam.getType(), method, bean);
                exHandler.add(exceptionExecution);
            }

            // 排序
            exHandler.sort((o1, o2) -> {
                Class<?> type1 = o1.getExceptionType();
                Class<?> type2 = o2.getExceptionType();
                if (type1.equals(type2)) return 0;
                return type1.isAssignableFrom(type2) ? 1 : -1;
            });
        });
    }

    public ExceptionExecution getHandler(Exception e) {
        for (ExceptionExecution execution : exHandler) {
            if (execution.getExceptionType().isAssignableFrom(e.getClass())) {
                return execution;
            }
        }
        return null;
    }
}
